package com.wcs.exam.common.base.page;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * sql工具类
 *
 * @author wcs
 */
public final class SqlUtil {
    private SqlUtil() {
    }

    private static final Pattern ORDER_BY_PATTERN = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);

    /**
     * 默认每页记录数(20)
     */
    public static final int DEFAULT_PAGE_SIZE = 20;

    /**
     * 最大每页记录数(1000)
     */
    public static final int MAX_PAGE_SIZE = 1000;

    /**
     * 检测sql，防止sql注入
     *
     * @param sql sql
     * @return 正常返回sql；异常返回""
     */
    public static String checkSql(String sql) {
        String inj_str = "'|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|+|,";
        String[] str = inj_str.split("\\|");
        for (String s : str) {
            if (sql.contains(s)) {
                return "";
            }
        }
        return sql;
    }

    /**
     * 计算总页数
     *
     * @param totalCount 总记录数.
     * @param pageSize   每页记录数.
     * @return totalPage 总页数.
     */
    public static long countTotalPage(final long totalCount, final long pageSize) {
        if (totalCount == 0) {
            return 1;
        }
        if (totalCount % pageSize == 0) {
            // 刚好整除
            return totalCount / pageSize;
        } else {
            // 不能整除则总页数为：商 + 1
            return totalCount / pageSize + 1;
        }
    }

    /**
     * 校验当前页数pageCurrent<br/>
     * 1、先根据总记录数totalCount和每页记录数pageSize，计算出总页数totalPage<br/>
     * 2、判断页面提交过来的当前页数pageCurrent是否大于总页数totalPage，大于则返回totalPage<br/>
     * 3、判断pageCurrent是否小于1，小于则返回1<br/>
     * 4、其它则直接返回pageCurrent
     *
     * @param totalCount  要分页的总记录数
     * @param pageSize    每页记录数大小
     * @param pageCurrent 输入的当前页数
     * @return pageCurrent
     */
    public static long checkPageCurrent(long totalCount, long pageSize, long pageCurrent) {
        // 最大页数
        long totalPage = countTotalPage(totalCount, pageSize);
        // 当前页不能小于1（避免页面输入不正确值）
        if (pageCurrent > totalPage) {
            // 如果页面提交过来的页数大于总页数，则将当前页设为总页数
            // 此时要求totalPage要大于获等于1
            return Math.max(totalPage, 1);
        } else {
            return Math.max(pageCurrent, 1);
        }
    }

    /**
     * 校验页面输入的每页记录数pageSize是否合法<br/>
     * 1、当页面输入的每页记录数pageSize大于允许的最大每页记录数MAX_PAGE_SIZE时，返回MAX_PAGE_SIZE
     * 2、如果pageSize小于1，则返回默认的每页记录数DEFAULT_PAGE_SIZE
     *
     * @param pageSize 页面输入的每页记录数
     * @return checkPageSize
     */
    public static long checkPageSize(long pageSize) {
        if (pageSize > MAX_PAGE_SIZE) {
            return MAX_PAGE_SIZE;
        } else if (pageSize < 1) {
            return DEFAULT_PAGE_SIZE;
        } else {
            return pageSize;
        }
    }

    /**
     * 计算当前分页的开始记录的索引
     *
     * @param pageCurrent 当前第几页
     * @param pageSize    每页记录数
     * @return 当前页开始记录号
     */
    public static long countOffset(final long pageCurrent, final long pageSize) {
        return (pageCurrent - 1) * pageSize;
    }

    /**
     * 返回限制一条记录SQL语句
     *
     * @return limitSql
     */
    public static String limitOne() {
        return limitSql(1);
    }

    /**
     * 返回分页语句
     *
     * @param size 分页大小
     * @return limitSql
     */
    public static String limitSql(int size) {
        return " limit " + size;
    }

    /**
     * 返回分页语句
     *
     * @param size 分页大小
     * @return limitSql
     */
    public static String limitSql(long size) {
        return " limit " + size;
    }

    /**
     * 根据总记录数，对页面传来的分页参数进行校验，并返分页的SQL语句
     *
     * @param pageCurrent 当前页
     * @param pageSize    每页记录数
     * @param totalCount  总记录数
     * @return limitSql
     */
    public static String limitSql(long totalCount, long pageCurrent, long pageSize) {
        // 校验当前页数
        pageCurrent = checkPageCurrent(totalCount, pageSize, pageCurrent);
        // 校验每页记录数
        pageSize = checkPageSize(pageSize);
        return " limit " + countOffset(pageCurrent, pageSize) + "," + pageSize;
    }

    /**
     * 根据分页查询的SQL语句，获取统计总记录数的语句
     *
     * @param sql 分页查询的SQL
     * @return countSql
     */
    public static String countSql(String sql) {
        // 去除第一个from前的内容
        String countSql = sql.substring(sql.toLowerCase().indexOf("from"));
        return "select count(*) " + removeOrderBy(countSql);
    }

    /**
     * 移除SQL语句中的的order by子句（用于分页前获取总记录数，不需要排序）
     *
     * @param sql 原始SQL
     * @return 去除order by子句后的内容
     */
    private static String removeOrderBy(String sql) {
        Matcher mc = ORDER_BY_PATTERN.matcher(sql);
        StringBuilder strBuf = new StringBuilder();
        while (mc.find()) {
            mc.appendReplacement(strBuf, "");
        }
        mc.appendTail(strBuf);
        return strBuf.toString();
    }

    /**
     * 模糊查询
     *
     * @param str sql
     */
    public static String like(String str) {
        return "%" + str + "%";
    }

    /**
     * 将驼峰命名风格的字符串转换为下划线连接风格的字符串。
     *
     * @param camelCaseStr 驼峰命名风格的字符串
     * @return 转换后的下划线连接风格字符串
     */
    public static String camelToUnderscore(String camelCaseStr) {
        if (camelCaseStr == null || camelCaseStr.isEmpty()) {
            return camelCaseStr;
        }
        StringBuilder sb = new StringBuilder(camelCaseStr.length());
        for (int i = 0; i < camelCaseStr.length(); i++) {
            char ch = camelCaseStr.charAt(i);
            if (Character.isUpperCase(ch)) {
                if (i > 0) {
                    sb.append('_');
                }
                sb.append(Character.toLowerCase(ch));
            } else {
                sb.append(ch);
            }
        }
        return sb.toString();
    }

}
